import {
    def,
    isObject,
    hasProto
} from '../uitl/index.js';

// 获取设置拦截后的方法
import { arrayMethods } from './array'

import Dep from './dep'

const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
/**
 * Observer class that is attached to each observed
 * object. Once attached, the observer converts the target
 * object's property keys into getter/setters that
 * collect dependencies and dispatch updates.
 */
/*
    附加到每个被观察对象的观察者类Observer，
    一旦连接上,obsever就会转换目标对象的属性到getter/setter收集依赖并分发更新
*/
export class Observer{
    constructor(value){
        this.value = value;
        // 给value新增一个__ob__属性，值为该value的Observer实例
        // 相当于为value打上标记，表示它已经被转换成响应式了，避免重复操作
        def(value,'__ob__',this);
        // 判断是不是数组
        if(Array.isArray(value)){
            const augment = hasProto?protoAugment:copyAugment;
            augment(value, arrayMethods, arrayKeys);
        }
        else{
            // value是Object执行walk方法遍历Object的的属性
            this.walk(value);
        }
    }

    /**
     * Walk through all properties and convert them into
     * getter/setters. This method should only be called when
     * value type is Object.
    */
    /*
        遍历所有属性将其转换为getter/setter
        这个方法只应在value的type为Object时调用
    */
   /**
    * 
    * @param {object} obj 
    */
    walk(obj){
        const keys = Object.keys(obj);
        for(let i = 0; i < keys.length; i++){
            defineReactive(obj,keys[i]);
        }
    }

    /**
     * Observe a list of Array items.
     */
    /**
     * 
     * @param {Array<any>} items 
     */
    observeArray (items){
        for (let i = 0; i < items.length; i++) {
            observe(items[i]);
        }
    }
}

/**
 * Augment an target Object or Array by intercepting
 * the prototype chain using __proto__
 */
/**
 * 如果浏览器支持使用__proto__
 * 则直接覆盖__proto__
 */
/**
 * 
 * @param {*} target 
 * @param {object} src 
 * @param {Array<string>} keys 
 */
function protoAugment(target, src, keys){
    target.__proto__ = src;
}

/**
 * Augment an target Object or Array by defining
 * hidden properties.
 */
/* istanbul ignore next */
function copyAugment(target,src,keys){
    for(let i = 0; i < keys.length; i++){
        const key = keys[i];
        def(target, key, src[key]);
    }
}


/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 */
/**
 * 尝试为一个值创建observer实例 
 * 如果成功创建，则返回创建的实例
 * 如果value已经存在一个Observer实例，则直接返回它
 */
/**
 * @param {any} value 
 * @param {boolean} asRootData 
 */
export function observe (value,asRootData){
    // 判断是否是对象，同时判断是否是VNode
    if(!isObject(value) || value instanceof VNode){
        return;
    }
    // observe实例
    let ob;
    // 判断属性是否已经是一个可观测的对象
    if(hasOwn(value, '__ob__') && value.__ob__ instanceof Observer){
        ob = value.__ob__;
    }else{
        ob = new Observer(value);
    }
    return ob;
}

/**
 * Define a reactive property on an Object
 */
/**
 * 使一个对象转化成可观测对象
 * customSetter:自定义设置的方法
 * shallow:是否遍历子对象
 */
/**
 * 使一个对象转化成可观测对象   
 * @param { Object } obj 对象
 * @param { String } key 对象的key
 * @param { Any } val 对象的某个key的值
 * @param {?boolean} shallow 是浅监听 -
 */
export function defineReacitive(obj,key,val,customSetter,shallow){
    // 创建一个依赖收集器
    const dep = new Dep();

    // 获取需要转换属性的描述信息
    const property = Object.getOwnPropertyDescriptor(obj,key);

    // 判断该属性是否允许配置
    if(property && property.configurable === false){
        return;
    }

    // cater for pre-defined getter/setters
    // 满足预定义的 getter/setters
    const getter = property && property.get;
    const setter = property && property.set;

    // 不存在getter或者存在setter 判断是否传进来了第3个参数val
    if((!getter || setter) && arguments.length === 2){
        val = obj[key];
    }

    // 深度监听
    let childOb = !shallow && observer(val);

    Object.defineProperty(obj, key,{
        enumable:true,
        configurable:true,
        get:function reactiveGetter(){
            const value = getter ? getter.call(obj) : val;
            if(Dep.target){
                dep.depend();
                if(childOb){
                    childOb.dep.depend();
                    if(Array.isArray(value)){
                        dependArray(value);
                    }
                }
            }
            return value;
        },
        set:function reactiveSetter(newVal){
            
            /* eslint-disable no-self-compare */
            /**
             * 判断新值和旧值是否相等,相等则不更新
             * newVal !== newVal && value !== value 是为了判断newVal和value均为NaN的情况
             * NaN!==NaN
             */
            if(newVal === val || (newVal !== newVal && value !== value)){
                return;
            }
            /* eslint-enable no-self-compare */
            // 判断属性是否有setter
            if(getter && !setter) return;
            if(setter){
                setter.call(obj,newVal);
            }else{
                val = newVal
            }
            // 深度监听新赋予的值
            childOb = !shallow && observer(newVal);
            // 在setter中通知依赖更新
            dep.notify();
        }

    })
}

// 收集数组中的数组的依赖
function dependArray(value){
    for (let i = 0; i < value.length; i++) {
        let e = value[i];
        // 判断
        e && e.__ob__ && e.__ob__.dep.depend();
        if(Array.isArray){
            dependArray(e);
        }
        
    }
}
